package cn.sat.miniodemo.controller;

import cn.sat.miniodemo.entity.OssFile;
import cn.sat.miniodemo.template.MinioTemplate;
import io.micrometer.common.util.StringUtils;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/file")
@AllArgsConstructor
public class FileController {
    private static final Logger log = LoggerFactory.getLogger(FileController.class);
    @Autowired
    MinioTemplate minioTemplate;
    @Autowired
    MinioClient minioClient;

    @PostMapping("/upload")
    @ResponseBody
    public List<OssFile> upload(
            @RequestParam("file") MultipartFile[] files,  // 修改为数组
            @RequestParam(value = "bucketName", required = false) String bucketName) throws IOException {

        List<OssFile> results = new ArrayList<>();
        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                OssFile ossFile = minioTemplate.putObject(file.getInputStream(), bucketName, file.getOriginalFilename());
                results.add(ossFile);
            }
        }
        return results;
    }


    /**
     * 支持断点续传的文件下载接口，根据客户端请求中的 Range 头部返回指定范围的数据。
     *
     * @param objectName 要下载的文件对象名称
     * @param range      HTTP 请求中的 Range 头部信息，格式如 "bytes=0-1023"，可为空表示下载整个文件
     * @return ResponseEntity 包含响应头和流式数据体，支持分段传输
     * @throws Exception 当 MinIO 操作或 IO 异常时抛出
     */
    @GetMapping("/download-range")
    public ResponseEntity<StreamingResponseBody> downloadRange(
            @RequestParam String objectName,
            @RequestHeader(value = "Range", required = false) String range) throws Exception {
        log.info("开始下载文件:{}", objectName);
        // 获取文件对象的基本信息（如大小）
        StatObjectResponse stat = minioClient.statObject(
                StatObjectArgs.builder().bucket("export-file").object(objectName).build()
        );
        long totalSize = stat.size();
        // 从元数据中获取原始文件名
        String originalFileName = stat.headers().get("original-filename");
        if (!StringUtils.isEmpty(objectName)) {
            // 如果元数据中没有，则从路径中提取
            originalFileName = objectName.substring(objectName.lastIndexOf("/") + 1);
        }
        // 设置响应头：内容类型为二进制流，支持字节范围请求
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
        headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");
        // 添加Content-Disposition头指定下载文件名
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + originalFileName + "\"");


        long start, end;
        // 解析 Range 请求头，确定数据传输的起始与结束位置
        if (range != null && range.startsWith("bytes=")) {
            log.info("开始断点续传下载文件:{}  {}", objectName, range);
            String[] ranges = range.substring(6).split("-");
            start = Long.parseLong(ranges[0]);
            end = ranges.length > 1 ? Long.parseLong(ranges[1]) : totalSize - 1;
            headers.add(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + end + "/" + totalSize);
            headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(end - start + 1));
        } else {
            start = 0;
            end = totalSize - 1;
        }

        // 构造流式响应体，用于将 MinIO 中的对象数据按指定范围写入 HTTP 响应输出流
        StreamingResponseBody stream = out -> {
            // 从MinIO服务器获取指定范围的文件对象输入流
            try (InputStream in = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket("root")
                            .object(objectName)
                            .offset(start)
                            .length(end - start + 1)
                            .build())) {
                // 将输入流中的数据传输到输出流
                in.transferTo(out);
            } catch (ServerException | InsufficientDataException | ErrorResponseException | NoSuchAlgorithmException |
                     InvalidKeyException | InvalidResponseException | XmlParserException | InternalException e) {
                throw new RuntimeException(e);
            }
        };
        log.info("结束断点续传下载文件:{}  {}", objectName, range);
        // 返回响应实体：若包含 Range 请求则返回 206 Partial Content，否则返回 200 OK
        return ResponseEntity.status(range != null ? 206 : 200)
                .headers(headers)
                .body(stream);
    }


    @PostMapping("/uploads")
    @ResponseBody
    public List<OssFile> batchUpload(
            @RequestParam("files") MultipartFile[] files,
            @RequestParam(value = "bucketName", required = false) String bucketName) throws IOException {

        List<InputStream> inputStreams = new ArrayList<>();
        List<String> originalFileNames = new ArrayList<>();

        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                inputStreams.add(file.getInputStream());
                originalFileNames.add(file.getOriginalFilename());
            }
        }

        return minioTemplate.putObjects(inputStreams, bucketName, originalFileNames);
    }

    @GetMapping("/listObjects")
    @ResponseBody
    public Iterable<Result<Item>> listObjects(
            @RequestParam(value = "bucketName", required = false) String bucketName) throws IOException {
        return minioTemplate.listObjects(bucketName,false);
    }



}